!>
module rerank_io

    use seakeeping_string, only: to_string
    use seakeeping_error_handling, only: fatal_error
    use seakeeping_filesystem, only: countlines
    use seakeeping_utils, only: incr
    use seakeeping_math, only: arange
    use rerank_string, only: string_t
    implicit none
    private

    public :: read_ranks, read_papers, write_papers, write_ranks

contains

    !> 读取文章中原排序
    subroutine read_ranks(filename, ranks)
        character(*), intent(in) :: filename            !! 原排序文件名
        integer, allocatable, intent(out) :: ranks(:)   !! 排序
        integer :: fid, nlines, i

        open (newunit=fid, file=filename, status='old', action='read')
        nlines = countlines(filename)
        allocate (ranks(nlines))

        do i = 1, nlines
            read (fid, *) ranks(i)
        end do
        close (fid)

    end subroutine read_ranks

    !> 读取文献
    subroutine read_papers(filename, papers)
        character(*), intent(in) :: filename       !! 文献文件名
        type(string_t), allocatable, intent(out) :: papers(:)   !! 文献
        integer :: fid, nlines, i
        character(512) :: cache

        open (newunit=fid, file=filename, status='old', action='read')
        nlines = countlines(filename)
        allocate (papers(nlines))

        print *, '*** Reading papers from file "'//filename//'" ***'
        do i = 1, nlines
            read (fid, '(A512)') cache
            allocate (papers(i)%raw, source=trim(cache))
            ! 检测读入文献
            print *, papers(i)%raw

            papers(i)%raw = trim(adjustl(papers(i)%raw(index(papers(i)%raw, ']') + 1:)))   ! 移除序号头部
        end do
        close (fid)

    end subroutine read_papers

    !> 输出重新排序的文献
    subroutine write_papers(filename, ranks, papers, tblfilename)
        character(*), intent(in) :: filename       !! 文献文件名
        integer, intent(in) :: ranks(:)            !! 排序
        type(string_t), intent(in) :: papers(:)    !! 文献
        character(*), intent(in) :: tblfilename    !! 表格索引文件名
        integer :: fid, i, ik, fid2
        logical, allocatable :: flag(:)     ! 不对重复出现的论文进行重排序
        character(:), allocatable :: cache

        allocate (flag(size(papers)), source=.false.)

        open (newunit=fid, file=filename, status='replace', action='write', form='formatted')
        open (newunit=fid2, file=tblfilename, status='replace', action='write', form='formatted')
        print *, '*** Writing new papers to file "'//filename//'" ***'
        ik = 0
        do i = 1, size(ranks)

            if (ranks(i) > size(flag)) call fatal_error("rank out of range")
            if (flag(ranks(i))) cycle
            flag(ranks(i)) = .true.
            call incr(ik)

            cache = '['//to_string(ik, '(i0)')//'] '//papers(ranks(i))%raw
            write (fid, '(a)') cache
            write (fid2, *) ranks(i), ik
            print *, cache

        end do
        close (fid)
        close (fid2)

    end subroutine write_papers

    !> 读取文本中的 ranks
    subroutine write_ranks(filename, outfilename)
        character(*), intent(in) :: filename, outfilename      !! 文本文件名
        character(:), allocatable :: cache
        integer :: stat, i, j, ik, fid
        integer, allocatable :: nums(:)

        open (newunit=fid, file=outfilename, status='replace', action='write', form='formatted')
        call read_whole_file(filename, cache, stat)
        if (stat /= 0) call fatal_error("failed to read file '"//filename//"'")

        do i = 1, len(cache)
            if (cache(i:i) == '[') then
                ik = i
                cycle
            end if

            if (cache(i:i) == ']') then
                if (index(cache(ik:i), '-') /= 0) then
                    nums = to_nums(cache(ik + 1:i - 1))
                    do j = 1, size(nums)
                        write (fid, '(i0)') nums(j)
                    end do
                else
                    write (fid, '(a)') cache(ik + 1:i - 1)
                end if
            end if

        end do
        close (fid)

    end subroutine write_ranks

    !> Read a whole file into an array of characters
    subroutine read_whole_file(filename, string, stat)
        !> File to read
        character(*), intent(in) :: filename
        !> Array of characters representing the file
        character(:), allocatable, intent(out) :: string
        !> Error status
        integer, intent(out) :: stat

        integer :: io, length

        open (file=filename, &
           & status="old", &
           & access="stream", &
           & position="append", &
           & newunit=io, &
           & iostat=stat)
        if (stat == 0) then
            inquire (unit=io, pos=length)
            allocate (character(length - 1) :: string, stat=stat)
        end if
        if (stat == 0) then
            read (io, pos=1, iostat=stat) string(:length - 1)
        end if
        if (stat == 0) then
            close (io)
        end if
    end subroutine read_whole_file

    function to_nums(string) result(nums)
        character(*), intent(in) :: string
        integer, allocatable :: nums(:)
        integer :: i, j, ik

        ik = index(string, '-')
        read (string(:ik - 1), *) i
        read (string(ik + 1:), *) j
        nums = arange(i, j)

    end function to_nums

end module rerank_io
